﻿using ZR.Model;
using ZR.Model.Souvenir;
using ZR.Model.Souvenir.Dto;
using ZR.Service.Souvenir.IService;
using ZR.ServiceCore.Model;

namespace ZR.Service.Souvenir
{
    [AppService(ServiceType = typeof(IOrderService), ServiceLifetime = LifeTime.Transient)]
    public class OrderService : BaseService<Order>, IOrderService
    {
        private readonly IOrderItemService _orderItemService;
        private readonly ICartService _cartService;

        public OrderService(IOrderItemService orderItemService, ICartService cartService)
        {
            _orderItemService = orderItemService;
            _cartService = cartService;
        }

        public async Task<int> InsertOrderAsync(Order order)
        {
            try
            {
                await Context.Ado.BeginTranAsync();
                order.PaymentTime = DateTime.Now;
                var res = await InsertNav(order)
                    .Include(it => it.OrderItems)
                    .ExecuteCommandAsync() ? 1 : 0;
                var productIds = order.OrderItems.Select(it => it.ProductId).ToList();
                await _cartService.Deleteable()
                    .Where(c => c.UserId == order.UserId)
                    .Where(c => productIds.Contains(c.ProductId))
                    .ExecuteCommandAsync();
                await Context.Ado.CommitTranAsync();
                return res;
            }
            catch (Exception e)
            {
                await Context.Ado.RollbackTranAsync();
                throw;
            }
        }

        public async Task<int> UpdateOrderAsync(Order order)
        {
            return await Updateable(order).ExecuteCommandAsync();
        }

        public async Task<int> DeleteOrderAsync(int orderId)
        {
            return await DeleteNav(it => it.Id == orderId)
                .Include(it => it.OrderItems)
                .ExecuteCommandAsync() ? 1 : 0;
        }

        public async Task<PagedInfo<OrderDto>> SelectOrderWithPageAsync(OrderQueryDto orderQueryDto)
        {
            RefAsync<int> total = 0;
            var exp = Expressionable.Create<Order>();
            exp.And(o => o.UserId == orderQueryDto.UserId);
            var res = await Queryable()
                .Includes(o => o.OrderItems)
                .Where(exp.ToExpression())
                .OrderByDescending(o => o.PaymentTime)
                .Select((o) => new OrderDto
                {
                    Id = o.Id,
                    UserId = o.UserId,
                    PaymentTime = o.PaymentTime,
                    Payment = o.Payment,
                    OrderItems = o.OrderItems
                        .Select(i => new OrderItemDto
                        {
                            Id = i.Id,
                            OrderId = i.OrderId,
                            ProductId = i.ProductId,
                            ProductName = i.ProductName,
                            CurrentUnitPrice = i.CurrentUnitPrice,
                            Quantity = i.Quantity,
                            TotalPrice = i.TotalPrice,
                        }).ToList()
                })
                .ToPageListAsync(orderQueryDto.PageNum, orderQueryDto.PageSize, total);
            foreach (var order in res)
            {
                foreach (var orderItem in order.OrderItems)
                {
                    var product = await Context.Queryable<Product>()
                        .LeftJoin<Category>((p, c) => p.CategoryId == c.Id)
                        .LeftJoin<SysFile>((p, c, f) => p.ImageId == f.Id)
                        .Where(p => p.Id == orderItem.ProductId)
                        .Select((p, c, f) => new ProductDto()
                        {
                            Name = p.Name,
                            ImageUrl = f.AccessUrl,
                            Detail = p.Detail,
                            CategoryName = c.Name
                        })
                        .FirstAsync();
                    if (product == null) continue;
                    orderItem.ProductDetail = product.Detail;
                    orderItem.ImageUrl = product.ImageUrl;
                    orderItem.CategoryName = product.CategoryName;
                    orderItem.ProductName = product.Name;
                }
            }
            var page = new PagedInfo<OrderDto>()
            {
                PageIndex = orderQueryDto.PageNum,
                PageSize = orderQueryDto.PageSize,
                Result = res,
                TotalNum = total
            };
            return page;
        }

        public async Task<Order> SelectOrderAsyncById(int orderId)
        {
            return await Queryable()
                .Where(it => it.Id == orderId)
                .FirstAsync();
        }

        public async Task<object> SelectSalesPerYearAsync(DateTime year)
        {
            var startTime = new DateTime(year.Year, 1, 1);
            var endTime = new DateTime(year.Year, 12, 31);
            if (year.Year == DateTime.Now.Year)
            {
                endTime = new DateTime(year.Year, DateTime.Now.Month, DateTime.Now.Day);
            }
            var month = new List<string>();
            var data = new List<decimal>();
            var allOrder = await Queryable()
                .ToListAsync();
            for (; startTime <= endTime;)
            {
                month.Add(startTime.ToString("yyyy-MM"));
                var order = allOrder.Where(o =>
                    o.PaymentTime >= startTime && o.PaymentTime <= startTime.AddMonths(1).AddSeconds(-1)).ToList();
                data.Add(order.Sum(o => o.Payment));
                startTime = startTime.AddMonths(1);
            }

            return new { month, data };
        }

        public async Task<object> SelectSalesPerMonthAsync(DateTime month)
        {
            var startTime = new DateTime(month.Year, month.Month, 1);
            var endTime = startTime.AddMonths(1).AddDays(-1);
            if (month.Year == DateTime.Now.Year && month.Month == DateTime.Now.Month)
            {
                endTime = new DateTime(month.Year, month.Month, DateTime.Now.Day);
            }
            var day = new List<string>();
            var data = new List<decimal>();
            var allOrder = await Queryable()
                .ToListAsync();
            for (; startTime <= endTime;)
            {
                day.Add(startTime.ToString("yyyy-MM-dd"));
                var order = allOrder.Where(o =>
                    o.PaymentTime >= startTime && o.PaymentTime <= startTime.AddDays(1).AddSeconds(-1)).ToList();
                data.Add(order.Sum(o => o.Payment));
                startTime = startTime.AddDays(1);
            }
            
            return new { day, data };
        }

        public async Task<object> SelectSalesWithCategoryAsync()
        {
            var allOrder = await Context.Queryable<OrderItem>()
                .LeftJoin<Product>((oi, p) => oi.ProductId == p.Id)
                .LeftJoin<Category>((oi, p, c) => p.CategoryId == c.Id)
                .GroupBy((oi, p, c) => new
                {
                    c.Id,
                    c.Name
                })
                .Select((oi, p, c) => new OrderItemDto()
                {
                    TotalPrice = SqlFunc.AggregateSum(oi.TotalPrice),
                    CategoryName = c.Name,
                    CategoryId = c.Id
                })
                
                .ToListAsync();
            // var category = await Context.Queryable<Category>()
            //     .ToListAsync();
            // var data = new List<decimal>();
            // var category = new List<string>();
            // var categorys = allOrder.Select(o => new Category()
            // {
            //     Id = o.CategoryId,
            //     Name = o.CategoryName
            // }).Distinct().ToList();
            // foreach (var item in categorys)
            // {
            //     var sum = allOrder.Where(o => o.CategoryId == item.Id)
            //         .Sum(o => o.TotalPrice);
            //     data.Add(sum);
            //     category.Add(item.Name);
            // }
            
            // return new { category = allOrder.Select(o => o.CategoryName).ToList(),
            //     data = allOrder.Select(o => o.TotalPrice).ToList() };

            return new
            {
                data = allOrder.Select(o => new
                {
                    Name = o.CategoryName,
                    Value = o.TotalPrice
                }).ToList()
            };
        }
    } 
}